package com.shihan.sqlSession;

import com.shihan.pojo.Configuration;
import com.shihan.pojo.MappedStatement;

import java.lang.reflect.*;
import java.sql.SQLException;
import java.util.List;

public class DefaultSQLSession implements SQLSession {

    private Configuration configuration;

    public DefaultSQLSession(Configuration configuration){
        this.configuration = configuration;
    }

    @Override
    public <E> List<E> selectList(String statementid, Object... params) throws Exception {
        SimpleExecutor simpleExecutor = new SimpleExecutor();

        MappedStatement statement = configuration.getMappedStatementMap().get(statementid);
        List<E> queryResult = simpleExecutor.query(configuration, statement, params);

        return queryResult;
    }

    @Override
    public <T> T selectOne(String statementid, Object... params) throws Exception {
        List<T> list = selectList(statementid, params);
        if(list!=null&&list.size()==1){
            return list.get(0);
        }else{
            throw new RuntimeException("查询结果为空，或结果集个数超过一个！");
        }
    }

    /**
     * 增、删、改方法，统一都是调用这一个方法即可，因为底层调用的都是 PreparedStatement 的 executeUpdate 方法
     * @param statemendit
     * @param params
     * @return
     */
    @Override
    public int executeUpdate(String statemendit, Object... params) throws ClassNotFoundException, SQLException, IllegalAccessException, NoSuchFieldException {
        // 底层执行 JDBC 的操作，依然封装到类中
        SimpleExecutor simpleExecutor = new SimpleExecutor();
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statemendit);
        int i = simpleExecutor.update(configuration, mappedStatement, params);

        return i;
    }

    /**
     * 创建 Dao 接口的 代理类对象，代替 DaoImpl 实现类
     * 通过代理对象调用方法时，最终都是要走 invoke 方法来执行方法的。
     * @param mapperClass
     * @param <T>
     * @return
     */
    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        // 通过 JDK 的动态代理，创建一个 代理对象，并且通过代理对象来调用方法
        Object proxyInstance = Proxy.newProxyInstance(DefaultSQLSession.class.getClassLoader(), new Class<?>[]{mapperClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 在 invoke 方法中，最终调用 selectList、selectOne 即可
                // 1、需要获取得到 statementId、params；PS：params == args
                String className = method.getDeclaringClass().getName();
                String methodName = method.getName();
                String statementId = className + "." + methodName;

                // 在此处进行判断，调用 executeQuery 方法，还是 executeUpdate 方法；
                MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
                // 如果调用的是 executeQuery，那么继续判断返回的是 List 还是 普通 Object
                if(mappedStatement.isQuery()){

                    // 2、接下来就要去 调用 selectList、selectOne 了
                    //    但是究竟是调用哪一个，还需要通过 调用方法的返回值结果来决定
                    Type genericReturnType = method.getGenericReturnType();
                    // 3、判断方法的返回值类型，如果有泛型类型的，那么认为是 List<T> 集合类型的
                    if(genericReturnType instanceof ParameterizedType){
                        List<Object> objects = selectList(statementId, args);
                        return objects;
                    }

                    // 方法返回值类型不是泛型类型，认为是普通 对象类型
                    Object o = selectOne(statementId, args);
                    return o;

                }else{ // 将调用 executeUpdate 方法，实现 insert、update、delete 操作
                    int i = executeUpdate(statementId, args);
                    return i;
                }

            }
        });

        return (T) proxyInstance;
    }
}
